#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <unistd.h> //read

#include <sys/types.h> //send
#include <sys/socket.h>

#include <sys/shm.h>
#include <pthread.h>
#include "mutexs.h"
#include "module.h"
#include "tcp_server.h"

#define REQ_DATA_SIZE 128
#define HDR_DATA_SIZE 128

#include <iostream>
#include <dirent.h>
#include "object_detect.h"
#include "utils.h"

char jpg_source_buf[JPG_MAX_SIZE];
unsigned int jpg_source_size;
char jpg_result_buf[JPG_MAX_SIZE];
unsigned int jpg_result_size;

unsigned int jpg_source_flag = 0;
unsigned int jpg_result_flag = 0;
unsigned int client_exit_flag = 0;

int convert_jpg_to_mat(const char *jpg_buf, const unsigned int &jpg_size, cv::Mat &mat);

int convert_mat_to_jpg(char *jpg_buf, unsigned int &jpg_size, cv::Mat &mat);

ImageData matToImageData(const cv::Mat &mat);

cv::Mat imageDataToMat(const ImageData &imageData);

ImageData charBufToImageData(const char *buf, int width, int height, int channels);

using namespace std;

namespace
{
	uint32_t g_modelWidth = 416;
	uint32_t g_modelHeight = 416;

}

int zgbfd, srvfd;

void *thread_acl(void *arg)
{
	const char *g_modelPath = "../model/yolo.om";
	// Check the input when the application executes, which takes the path to the input video file

	ObjectDetect detect(g_modelPath, g_modelWidth, g_modelHeight);
	Result ret = detect.Init();
	if (ret != SUCCESS)
	{
		ERROR_LOG("Classification Init resource failed");
		return (void *)FAILED;
	}

	// Frame by frame reasoning
	while (1)
	{
		if (client_exit_flag)
			pthread_exit(NULL);
		if (jpg_source_flag)
		{
			cv::Mat frame;

			ImageData src_img, resized_img;

			pthread_mutex_lock(&cam_mutex);
			/*
			if (0 > convert_jpg_to_mat(jpg_source_buf, jpg_source_size, frame))
			{
				fprintf(stdout, "convert_jpg_to_mat failed");
			}
			*/
			src_img = charBufToImageData(jpg_source_buf, 640, 480, 3);
			pthread_mutex_unlock(&cam_mutex);

			// cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB);
			//  The frame image is preprocessed
			Result ret = detect.Preprocess(resized_img, src_img);

			if (ret != SUCCESS)
			{
				ERROR_LOG("Read frame failed, continue to read next");
				continue;
			}
			// The preprocessed images are fed into model reasoning and the reasoning results are obtained
			aclmdlDataset *inferenceOutput = nullptr;
			ret = detect.Inference(inferenceOutput, resized_img);
			if ((ret != SUCCESS) || (inferenceOutput == nullptr))
			{
				ERROR_LOG("Inference model inference output data failed");
				return (void *)FAILED;
			}
			ret = detect.Postprocess(frame, inferenceOutput, " ");
			if (ret != SUCCESS)
			{
				ERROR_LOG("Process model inference output data failed");
				return (void *)FAILED;
			}
			pthread_mutex_lock(&send_mutex);

			if (0 > convert_mat_to_jpg(jpg_result_buf, jpg_result_size, frame))
			{
				fprintf(stdout, "convert_mat_to_jpg failed");
			}
			pthread_mutex_unlock(&send_mutex);

			fprintf(stdout, "--------inference success!--------\n");
			jpg_source_flag = 0; // 避免同张图片重复处理
			jpg_result_flag = 1; // 推理后数据就绪，标志可以发送
		}
	}

	/*
	test net recv and send data
	*/
	// while (1)
	// {
	// 	if (jpg_source_flag)
	// 	{
	// 		pthread_mutex_lock(&cam_mutex);
	// 		pthread_mutex_lock(&send_mutex);
	// 		fprintf(stdout, "pthread_acl ->  jpg_source_size=%d, ", jpg_source_size);
	// 		jpg_result_size = jpg_source_size;
	// 		strncpy(jpg_result_buf, jpg_source_buf, jpg_source_size);
	// 		fprintf(stdout, "jpg_result_size=%d\n", jpg_result_size);
	// 		pthread_mutex_unlock(&send_mutex);
	// 		pthread_mutex_unlock(&cam_mutex);
	// 		jpg_source_flag = 0;
	// 		jpg_result_flag = 1;
	// 	}
	// }

	return (void *)SUCCESS;
}

void *thread_zgb(void *arg)
{
	return (void *)0;
}

void *thread_srv(void *arg)
{
	int *tempfd;
	int connfd;
	pthread_t requst_tid;
	pthread_t sendtoc_tid;

	int s = tcp_server_init(8080, 16);
	if (0 > s)
	{
		printf("init_server failed!\n");
		pthread_exit(NULL);
	}
	while (1)
	{

		if ((connfd = tcp_server_wait_connect(s)) != -1)
		{
			tempfd = new int;
			if (!tempfd)
			{
				fprintf(stderr, "server->malloc: malloc failure\n");
				close(connfd);
				close(s);
			}
			if (client_exit_flag)
				client_exit_flag = 0;
			*tempfd = connfd;
			fprintf(stdout, "connfd=%d\n", connfd);
			int ret = pthread_create(&requst_tid, NULL, Read_request, &connfd);
			sleep(1);
			if (ret)
			{
				errno = ret;
				perror("server->thread");
				close(connfd);
				close(s);
			}
			printf("server->thread: create client recv thread success\n");
			// pthread_detach(requst_tid);

			ret = pthread_create(&sendtoc_tid, NULL, Send_toclient, &connfd);
			sleep(1);
			if (ret)
			{
				errno = ret;
				perror("server->thread");
				close(connfd);
				close(s);
			}
			printf("server->thread: create client send thread success\n");
			// pthread_detach(sendtoc_tid);
		}
	}
	return (void *)0;
}
void *Send_toclient(void *arg)
{
	int connfd = *(int *)arg;
	char request[REQ_DATA_SIZE] = {0};
	fprintf(stdout, "entry send pthread\n");
	while (1)
	{
		if (!jpg_result_flag)
		{
			continue;
		}
		if (client_exit_flag)
			pthread_exit(NULL);
		fprintf(stdout, "entry send while\n");
		memset(&request, 0, sizeof(request));
		// recv postprocess jpg size
		pthread_mutex_lock(&send_mutex);
		if (jpg_result_size <= 0)
		{
			fprintf(stdout, "send_toclinet continue\n");
			continue;
		}
		sprintf(request, "%d", jpg_result_size);

		int ret = tcp_server_send_exact_nbytes(connfd, (void *)request, sizeof(request));
		if (ret < 0)
		{
			fprintf(stderr, "client->read: recv jpg_show_size failed\n");
			return (void *)-1;
		}

		// recv postprocess jpg data
		ret = tcp_server_send_exact_nbytes(connfd, (void *)jpg_result_buf, jpg_result_size);
		if (ret < 0)
		{
			fprintf(stderr, "client->read: recv jpg_show_buf failed\n");
			return (void *)-1;
		}

		fprintf(stdout, "jpg_result_size=%d, send pic, size=%d\n", jpg_result_size, ret);
		pthread_mutex_unlock(&send_mutex);

		jpg_result_flag = 0; // 避免同张图片重复发送
	}
	return (void *)0;
}
void *Read_request(void *arg)
{
	int ret;
	int connfd = *(int *)arg;
	char response[HDR_DATA_SIZE] = {0};
	fprintf(stdout, "entry recv pthread\n");
	int i = 1;
	while (i)
	{
		memset(&response, 0, sizeof(response));
		fprintf(stdout, "entry recv while\n");
		ret = tcp_server_recv_exact_nbytes(connfd, response, sizeof(response));
		if (ret < 0)
		{
			printf("recv jpg_size failed\n");
			continue;
		}

		pthread_mutex_lock(&cam_mutex);
		jpg_source_size = atoi(response);
		printf("recv %d bytes data\n", jpg_source_size);
		ret = tcp_server_recv_exact_nbytes(connfd, (void *)jpg_source_buf, jpg_source_size);

		if (ret < 0)
		{
			printf("recv jpg_buf failed\n");
		}
		else if (ret < jpg_source_size)
		{
			printf("client %d exit!\n", connfd);
			client_exit_flag = 1;
			pthread_mutex_unlock(&cam_mutex);
			pthread_exit(NULL);
		}
		else
		{
			printf("recv jpg_buf suceess\n");
			jpg_source_flag = 1;
		}
		pthread_mutex_unlock(&cam_mutex);
	}
	return (void *)0;
}

int convert_jpg_to_mat(const char *jpg_buf, const unsigned int &jpg_size, cv::Mat &mat)
{
	// 将内存中的JPG数据转换为cv::Mat对象
	std::vector<u_char> input_buffer(jpg_buf, jpg_buf + jpg_size);
	mat = cv::imdecode(input_buffer, cv::IMREAD_COLOR);

	// 检查是否成功解码图片
	if (mat.empty())
	{
		std::cerr << "Error: Could not decode the JPG image." << std::endl;
		return -1;
	}
	return 0;
}

int convert_mat_to_jpg(char *jpg_buf, unsigned int &jpg_size, cv::Mat &mat)
{
	std::vector<u_char> buffer;
	int result = cv::imencode(".jpg", mat, buffer);
	if (result)
	{

		memcpy(const_cast<char *>(jpg_buf), buffer.data(), buffer.size());
		jpg_size = buffer.size();
		return 0;
	}
	else
	{
		std::cerr << "Error: Failed to encode the image to JPG format." << std::endl;
		return -1;
	}
}

// 将 cv::Mat 转换为 ImageData
ImageData matToImageData(const cv::Mat &mat)
{
	ImageData imageData;
	imageData.width = mat.cols;
	imageData.height = mat.rows;
	imageData.alignWidth = mat.cols;
	imageData.alignHeight = mat.rows;
	imageData.size = mat.total() * mat.elemSize();
	imageData.data = std::shared_ptr<uint8_t>(new uint8_t[imageData.size], std::default_delete<uint8_t[]>());
	memcpy(imageData.data.get(), mat.data, imageData.size);
	return imageData;
}

// 将 ImageData 转换为 cv::Mat
cv::Mat imageDataToMat(const ImageData &imageData)
{
	cv::Mat mat(imageData.height, imageData.width, CV_8UC3, imageData.data.get());
	return mat;
}

ImageData charBufToImageData(const char *buf, int width, int height, int channels)
{
	ImageData imageData;
	imageData.width = width;
	imageData.height = height;
	imageData.alignWidth = width;
	imageData.alignHeight = height;
	imageData.size = width * height * channels;
	imageData.data = std::shared_ptr<uint8_t>(new uint8_t[imageData.size], std::default_delete<uint8_t[]>());
	memcpy(imageData.data.get(), buf, imageData.size);
	return imageData;
}